using System;
using System.Diagnostics;
using System.Drawing;
using System.Net;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
using Scrobbler;

namespace Bbc.Scrobblers
{
    public abstract class BbcStreamSource : StreamSource, IStreamSource
    {
        public override void GetLatestSong()
        {
            Debug.Assert(!String.IsNullOrEmpty(NowPlayingUrl));

            using (var client = new WebClient())
            {
                if (!string.IsNullOrEmpty(GlobalConfiguration.Configuration.ProxyAddress))
                {
                    client.Encoding = Encoding.UTF8;
                    var proxy = new WebProxy(GlobalConfiguration.Configuration.ProxyAddress, false) { UseDefaultCredentials = true };
                    client.Proxy = proxy;
                }

                client.DownloadStringCompleted += (sender, args) => GotSong(args);
                client.DownloadStringAsync(new Uri(NowPlayingUrl));
            }
        }

        private void GotSong(DownloadStringCompletedEventArgs eventArgs)
        {
            string result = string.Empty;

            try
            {
                result = eventArgs.Result;
            }
            catch (TargetInvocationException ex)
            {
                // No connection to the host
                OnErrorOccured(ex.InnerException == null
                                   ? new ProcessorEventArgs(ex.Message)
                                   : new ProcessorEventArgs(ex.InnerException.Message));
                return;
            }

            ISong song = null;

            try
            {
                song = DeserializeAndGetCurrentSong(result);
            }
            catch (SerializationException)
            {
                // Serialization exception. Occurs often if radio doesn't provide the current track at the moment
                OnErrorOccured(new ProcessorEventArgs("Cannot read the source"));
                return;
            }

            bool validated = ValidateSong(song);
            if (!validated) return;

            PostProcess(song);
            var songEventHandlerArgs = new SongEventArgs(song);

            if (IsSongChanged(song))
            {
                CurrentSong = song;
                OnSongChanged(songEventHandlerArgs);
            }

            OnGotSong(songEventHandlerArgs);
        }

        internal virtual bool IsSongChanged(ISong song)
        {
            if (song == null) Debug.Fail("song parameter cannot be null");
            if (CurrentSong == null) return true;
            return !(CurrentSong.Artist.Equals(song.Artist) && CurrentSong.Title.Equals(song.Title));
        }

        internal virtual bool ValidateSong(ISong song)
        {
            return SongValidator.Validate(song);
        }

        internal virtual ISong PostProcess(ISong song)
        {
            ReplaceInSongArtistAndTitle(song, "&amp;", "&");
            return song;
        }

        private void ReplaceInSongArtistAndTitle(ISong song, string oldValue, string newValue)
        {
            song.Artist = song.Artist.Replace(oldValue, newValue);
            song.Title = song.Title.Replace(oldValue, newValue);
        }

        internal virtual TimeSpan GetDuration()
        {
            // If Duration cannot be read, let's take the defined one:
            return new TimeSpan(0, 2, 0);
        }

        internal abstract string NowPlayingUrl { get; }

        internal abstract ISong DeserializeAndGetCurrentSong(string doc);
    }
}